//
//  ViewController.m
//  手写板蓝牙
//
//  Created by Hello Cai on 16/5/22.
//  Copyright © 2016年 Hello Cai. All rights reserved.
//

#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

//外设向中央设备广播数据的特征
#define kCharacteristicUUID @"CEB88D44-1DE0-4B09-A17D-A2BF9DC7B815"
//留给中央设备向外设写数据的特征(中央用该特征发数据)
#define kCharacteristicUUIDCentralWr @"CEB88D44-1DE0-4B09-A17D-A2BF9DC7B816"
#define kServiceUUID @"F37B9988-B5B6-42D9-BC88-A15672743070"

//说明文中说的外设指的是周年设备，用来发送数据
/*
 如何实现一个周边设备？如何用mac or ios程序模拟外设
 1.声明CBPeripheralManager周边设备管理器
 2.实例化CBPeripheralManager
 3.让当前VC遵从CBPeripheralManagerDelegate
 4.当成功实例化CBPeripheralManager对象后，如果蓝牙设备通电了，会调用回调函数peripheralManagerDidUpdateState
 5.当外设连通电源后，实例化特征（CBMutableCharacteristic）和服务（CBMutableServices），将特征
 添加到服务上，将服添加到外设管理器中
 6.添加服务成功后，开始广播
 7.当成功广播后，调用peripheralManagerDidStartAdvertising：如果没有错误，此时外设可以被中央设备
 发现了
 8.当其他中央设备扫描到此外设，并且主链接到此外设回调，从现在开始，外设可以向中央设备发送数据了
 9.用户在窗口拖动鼠标，将坐标点发送给中央设备
 
 */
@interface ViewController ()<CBPeripheralManagerDelegate>
//声明一个周边设备管理器属性
@property(nonatomic,strong)CBPeripheralManager *peripheralManager;
@property(nonatomic,strong)CBMutableCharacteristic *characteristic;
@property(nonatomic,strong)CBCentral *central;

@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    //实例化CBPeripheralManager一个周边设备
    [self createPeripheralManager];

}

-(void)createPeripheralManager{
    
    //参数1：代理者
    //参数2：当事件发生时，在哪个队列中调用代理者的方法，如果传nil就在当前队列，这里指的是主线程
    self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
    
}

//第四步，蓝牙接通情况
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    //判断蓝牙设备的状态
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
        NSLog(@"蓝牙设备已经接通电源");
        [self setUpServiceAndCharacteristic];
    }
    else {
        NSLog(@"蓝牙设备接通电源失败");
    }
}

//第五步，当蓝牙连接情况下，实例化特征和服务，将特征加到服务上，将服务加到外设管理器中
-(void)setUpServiceAndCharacteristic{
    //5.1创建特征 (一个服务可以多个特征)
    CBUUID *uuid = [CBUUID UUIDWithString:kCharacteristicUUID];
    //参数1：特征UUID
    //参数2：特征属性(读，写，通知)
    //参数3：value，当特征第一次接收到，包含的值，nil
    //参数4：本特征有哪些权限
        //该特征用来广播数据给中央设备（中央设备订阅该特征可接收外设的数据），即提供个特征用于发送数据到中央
    self.characteristic = [[CBMutableCharacteristic alloc] initWithType:uuid properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
    
        //该特征用于让中央设备写数据到特征值中 ，则外设可在“接收写入”的回调方法中获取到中央设备的数据，即提供个特征供中央往外设发(写)数据
    CBUUID *uuidCentralWr = [CBUUID UUIDWithString:kCharacteristicUUIDCentralWr];
    CBCharacteristic *centralChara = [[CBMutableCharacteristic alloc] initWithType:uuidCentralWr properties:CBCharacteristicPropertyWrite value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
    
    //5.2创建服务
    //参数1：服务UUID
    //参数2：是否是主服务
    CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
    CBMutableService *service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
    
    //5.3将特征添加到服务上
    [service setCharacteristics:@[self.characteristic, centralChara]];
    
    //5.4将服务添加到外设上
    [self.peripheralManager addService:service];
}

//6当服务添加到外设管理器后,回调
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
    if (error==nil) {
        
        NSLog(@"添加服务到外设成功");
        //开始广播
        //参数：字典  包含广播的特性属性
        //CBAdvertisementDataLocalNameKey : 本服务的名字
        //CBAdvertisementDataServiceUUIDsKey : 服务的UUID
        NSDictionary *advertisingDict = @{CBAdvertisementDataLocalNameKey:@"handWritingBoard",CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:kServiceUUID]]};
        //开启广播服务，成功的话会回调peripheralManagerDidStartAdvertising方法
        [self.peripheralManager startAdvertising:advertisingDict];
    }
    else{
        
        NSLog(@"添加服务到外设失败");
    }
}

//7.当成功开启广播后，回调peripheralManagerDidStartAdvertising
//如果没有错误，此时外设可以被中央设备发现了
-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
    
    if(!error){
        NSLog(@"成功开启了广播");
        
    }
    else{
        NSLog(@"开启广播失败");
    }
}

//8。当其他设备扫描到此外设，并且主链接到此外设，回调
//此时开始，外设可以向中央设备发送数据了
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
    
    NSLog(@"已经有中央：%@ 订阅特征",central);
    self.central = central;
}

//补充by Cai. 用来接收中央设备发送过来的数据，必须要有respondToRequest:，否则出现每10s外设才回调一次的奇怪现象
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests{
    CBATTRequest *request = requests[0];
    NSData *data = request.value;
    NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"writeRequest Message:%@",message);
    if (request.characteristic.properties & CBCharacteristicPropertyWrite) {//这句很重要
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}

-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
    NSData *data = request.value;
    NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"peripheralManager: didReceiveReadRequest：%@",message);
    //必须要有这句，否则不会回调
    [peripheral respondToRequest:request withResult:CBATTErrorSuccess];
}



//用户在窗口拖动鼠标，将坐标点发送给中央设备
-(void)mouseDragged:(NSEvent *)theEvent{
    
    //取鼠标在窗口上拖拽的位置
    CGPoint point = [theEvent locationInWindow];
    NSString *pointString = NSStringFromPoint(point);
//    NSLog(@"pointString%@",pointString);
    
    if(self.central !=nil){
        //发送坐标点给中央设备
        //参数1：发送的数据
        //参数2：从哪一个特征发送
        //参数3：发送到哪一个中央设备
        [self.peripheralManager updateValue:[pointString dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.characteristic onSubscribedCentrals:@[self.central]];
    }
}

- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}

@end
